﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using BotModels;
using HtmlAgilityPack;

namespace BotTalkLibrary
{
    public class RequestMaker
    {
        private const string BASE_URI = "http://www.pandorabots.com/pandora/talk-xml";
        private const string HTML_URI = "http://www.pandorabots.com/pandora/talk";
        private const string FAMOUS_BOTS_URL = "http://www.pandorabots.com/botmaster/en/mostactive";

        public const string WRONG_BOTID_ERROR_MESSAGE = "Failed to find bot";

        public event EventHandler<SendBotMessageEventArgs> SendBotMessageCompleted;
        public event EventHandler<GetBotNameEventArgs> GetBotNameCompleted;
        public event EventHandler<GetFamousBotsEventArgs> GetFamousBotsCompleted;

        private readonly WebClient client;
        private HtmlWeb famousBotsPage;

        public RequestMaker()
        {
            client = new WebClient();
        }

        public void SendBotMessage(string botId, string message)
        {
            SendBotMessage(botId, message, null);
        }

        public void SendBotMessage(string botId, string message, string customerId)
        {
            StringBuilder uri = new StringBuilder(BASE_URI);
            StringBuilder parameters = new StringBuilder("input=");

            parameters.Append(message);
            if (customerId != null)
            {
                parameters.AppendFormat("&custid={0}", customerId);
            }
            uri.AppendFormat("?botid={0}", botId);

            client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
            client.UploadStringCompleted += SendBotCallback;
            client.UploadStringAsync(new Uri(uri.ToString()), "POST", parameters.ToString());
        }

        public void GetBotName(string botId)
        {
            StringBuilder uri = new StringBuilder(HTML_URI);
            uri.AppendFormat("?botid={0}", botId);

            HtmlWeb webDoc = new HtmlWeb();
            webDoc.LoadCompleted += (sender, args) =>
                {
                    GetBotNameEventArgs response = new GetBotNameEventArgs();
                    response.Id = botId;
                    response.Name = ExtractBotNameFromHtml(args.Document);

                    if (GetBotNameCompleted != null)
                    {
                        GetBotNameCompleted(this, response);
                    }
                };
            
            webDoc.LoadAsync(uri.ToString());
        }

        public void GetFamousBots()
        {
            famousBotsPage = new HtmlWeb();
            famousBotsPage.LoadCompleted += FamousPageLoadCompleted;
            
            famousBotsPage.LoadAsync(FAMOUS_BOTS_URL);
        }

        #region Private methods

        private void SendBotCallback(object sender, UploadStringCompletedEventArgs args)
        {
            client.UploadStringCompleted -= SendBotCallback;
            SendBotMessageEventArgs response = new SendBotMessageEventArgs();

            if (args.Error != null)
            {
                response.Error = args.Error.Message;
            }
            else
            {
                response.Message.Content = GetAnswer(args.Result);
                response.ConversationId = GetConversationId(args.Result);
            }

            if (SendBotMessageCompleted != null)
            {
                SendBotMessageCompleted(this, response);
            }
        }

        private string GetAnswer(string xmlAnswer)
        {
            XDocument xml = XDocument.Parse(xmlAnswer);
            var title = (XElement)xml.Root.LastNode;

            return title.Value;
        }

        private string GetConversationId(string xmlAnswer)
        {
            try
            {
                XDocument xml = XDocument.Parse(xmlAnswer);
                return xml.Element("result").Attribute("custid").Value;
            } catch
            {
                return null;
            }
        }

        private string ExtractBotNameFromHtml(HtmlDocument htmlAnswer)
        {
            string title = htmlAnswer.DocumentNode.Element("html").Element("head").Element("title").InnerText;

            return title.Split(new[] {"Bot "}, StringSplitOptions.RemoveEmptyEntries).Last();
        }

        private void FamousPageLoadCompleted(object sender, HtmlDocumentLoadCompleted e)
        {
            if (GetFamousBotsCompleted != null)
            {
                if (e.Error == null)
                {
                    HtmlDocument doc = e.Document;

                    var botsRows = doc.DocumentNode.Element("html")
                               .Element("body")
                               .Element("table")
                               .Element("tr")
                               .Elements("td")
                               .Last()
                               .Elements("table")
                               .Last().Elements("tr");

                    List<HtmlNode> nodes = new List<HtmlNode>();
                    bool first = true;

                    foreach (HtmlNode htmlNode in botsRows)
                    {
                        if (htmlNode.NodeType == HtmlNodeType.Element && !first)
                            nodes.Add(htmlNode.Elements("td").First().Element("a"));
                        first = false;
                    }

                    List<Bot> bots = new List<Bot>();

                    foreach (HtmlNode botsLink in nodes)
                    {
                        Bot b = GetBotFromLink(botsLink);
                        if (b != null)
                            bots.Add(b);
                    }

                    GetFamousBotsCompleted(this, new GetFamousBotsEventArgs {Bots = bots});
                }
                else
                {
                    GetFamousBotsCompleted(this, new GetFamousBotsEventArgs { Error = e.Error });
                }

                famousBotsPage.LoadCompleted -= FamousPageLoadCompleted;
            }
        }

        private Bot GetBotFromLink(HtmlNode linkNode)
        {
            if (linkNode == null)
                return null;

            string botUrl = linkNode.GetAttributeValue("href", null);
            string botName = linkNode.InnerText;

            if (botUrl == null || string.IsNullOrEmpty(botName))
                return null;

            string botId = botUrl.Split(new[] {"botid="}, StringSplitOptions.RemoveEmptyEntries).Last();

            Bot b = new Bot { Id = botId, Name = botName };

            return b;
        }

        #endregion
    }
}